using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

using Verety.DirectoryService.Contacts;
using Verety.SharedMeta.DirectoryServiceMeta.Configuration;
using Verety.SharedMeta.DirectoryServiceMeta.Connection;

using SBPweb.Contracts;
using SBPweb.Contracts.Messaging;
using SBPweb.Modules.Modularity;
using SBPweb.Presentation.Windows.Forms.Controls;
using SBPweb.Contracts.Implementation;
using SBPweb.Modules.Smartparts;
using SBPweb.Modules.Messaging.Controls;

// This alias is needed for the module loader to recognize the module's entry point
[assembly: AssemblyDefaultAlias("SBPweb.Modules.Messaging.MessagingModule")]

namespace SBPweb.Modules.Messaging
{
    public class MessagingModule : ModuleBase, IMessagingService
	{
		#region Private & Protected members

		protected IStateManagementService pStateManagementService;
        protected Verety.DirectoryService.DirectoryService pDirectoryService;
        protected LabelItemControl pJID;
        protected DockingUserControl pDebugControl;
        private bool mFirstTry = true;
		private LoginDockingControl loginControl;
		private IEasterEgg debugEasterEgg;
        private string pMessage;
        private string pLastState;

		private IEasterEggHandlerService EasterEggHandler
		{
			get
			{
				if (Services[typeof(IEasterEggHandlerService)] == null)
				{
					return null;
				}
				return Services[typeof(IEasterEggHandlerService)] as IEasterEggHandlerService;
			}
		}

		#endregion

		#region Constructor logic

		public MessagingModule(IModuleHost host)
			: base(host)
		{
            InitAppConfig();

			Name = "SBPweb.Modules.Messaging";

			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWrite += new Verety.SharedMeta.MessagingServiceMeta.Log.LogWrite(LogManager_OnLogWrite);
			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWriteMultiCategory += new Verety.SharedMeta.MessagingServiceMeta.Log.LogWriteMultiCategory(LogManager_OnLogWriteMultiCategory);
			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWriteUniCategory += new Verety.SharedMeta.MessagingServiceMeta.Log.LogWriteUniCategory(LogManager_OnLogWriteUniCategory);

            pDirectoryService = new Verety.DirectoryService.DirectoryService();
            pDirectoryService.Initialize();
            Services.Add(typeof(IMessagingService), this);

            host.ModuleLoader.ModuleGroupLoaded += new EventHandler(ModuleLoader_ModuleGroupLoaded);

			#region Connection events

			pDirectoryService.Connection.OnDisconnect += new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnDisconnect);
			pDirectoryService.Connection.OnAuthenticate += new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnAuthenticate);
			pDirectoryService.Connection.OnAuthenticationError += new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnAuthenticationError);
			pDirectoryService.Connection.OnWarning += new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionErrorHandler(Connection_OnWarning);
			pDirectoryService.Connection.OnError += new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionErrorHandler(Connection_OnError);

			#endregion
		}

		#endregion

		#region Private helper methods

        private void InitAppConfig()
        {
            object configSystem = typeof(System.Configuration.ConfigurationManager).GetField("s_configSystem", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetValue(null);
            object configRecord = configSystem.GetType().GetField("_completeConfigRecord", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(configSystem);
            System.Collections.Hashtable sectionRecords = configRecord.GetType().GetField("_sectionRecords", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(configRecord) as System.Collections.Hashtable;

            String privatepath = System.Reflection.Assembly.GetAssembly(this.GetType()).Location;
            System.Configuration.Configuration config = System.Configuration.ConfigurationManager.OpenExeConfiguration(privatepath);

            object inputConfigRecord = config.GetType().GetField("_configRecord", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(config);
            System.Collections.Hashtable inputSectionRecords = inputConfigRecord.GetType().GetField("_sectionRecords", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(inputConfigRecord) as System.Collections.Hashtable;

            foreach (System.Collections.DictionaryEntry de in inputSectionRecords)
            {
                config.GetSection(de.Key.ToString());
            }

            if (inputSectionRecords["externalConfigurationSource"] != null && !sectionRecords.ContainsKey("externalConfigurationSource"))
            {
                sectionRecords.Add("externalConfigurationSource", inputSectionRecords["externalConfigurationSource"]);
            }
        }

        private delegate bool TryToLogInWithADUserHandler();

		private bool TryToLogInWithADUser()
		{
            if (loginControl.InvokeRequired)
            {
                return (bool)loginControl.Invoke(new TryToLogInWithADUserHandler(TryToLogInWithADUser));
            }

			//try
			//{
			//    if (Host.SecurityPrincipal.Identity.Name.Contains("\\"))
			//    {
			//        string[] user = Host.SecurityPrincipal.Identity.Name.Split("\\".ToCharArray());
			//        Connect(user[1], "passw0rd");
			//        return true;
			//    }
			//}
			//catch { }
			try
			{
				if (loginControl.CurrentUser != string.Empty && loginControl.IsSaveUserName && loginControl.IsSavePassword)
				{
					Connect(loginControl.CurrentUser, loginControl.CurrentPassword, loginControl.DirectoryServer, loginControl.ConferenceServer, loginControl.PubsubServer, loginControl.Port);
					loginControl.IsVisible = false;
					return true;
				}
			}
			catch { }
			return false;
		}

		#endregion

		#region Public properties

		public bool IsConnected
		{
			get
			{
				if (pDirectoryService != null && pDirectoryService.Connection != null && pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connected)
				{
					return true;
				}
				return false;
			}
		}

		public object ManagedDirectoryServiceObject
		{
			get
			{
				return pDirectoryService;
			}
		}

        public string Server
        {
            get
            {
                return pDirectoryService.Connection.Server;
            }
			set
			{
				if (pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connected ||
					pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connecting)
				{
					throw new ArgumentException("Cannot set the Directory Server when you are not disconnected!");
				}
				(pDirectoryService.Configuration as IDirectoryConfigurationManager).Server = value;
			}
		}

        public string UserName
        {
            get
            {
                return pDirectoryService.Connection.UserName;
            }
        }

        public string UserJid
        {
            get
            {
                return pDirectoryService.Connection.UserContactId.Resource;
            }
        }

		public string ConferenceServer
		{
			get
			{
				return (pDirectoryService.Configuration as IDirectoryConfigurationManager).ConferenceServer;
			}
			set
			{
				if (pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connected ||
					pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connecting)
				{
					throw new ArgumentException("Cannot set the Conference Server when you are not disconnected!");
				}
				(pDirectoryService.Configuration as IDirectoryConfigurationManager).ConferenceServer = value;
			}
		}

		public string PubsubServer
		{
			get
			{
				return (pDirectoryService.Configuration as IDirectoryConfigurationManager).PubSubServer;
			}
			set
			{
				if (pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connected ||
					pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connecting)
				{
					throw new ArgumentException("Cannot set the Pubsub Server when you are not disconnected!");
				}
				(pDirectoryService.Configuration as IDirectoryConfigurationManager).PubSubServer = value;
			}
		}

		public int Port
		{
			get
			{
				return pDirectoryService.Connection.Port;
			}
			set
			{
				if (pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connected ||
					pDirectoryService.Connection.CurrentConnectionState == Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionState.Connecting)
				{
					throw new ArgumentException("Cannot set the Server connection port when you are not disconnected!");
				}
				(pDirectoryService.Configuration as IDirectoryConfigurationManager).Port = value;
			}
		}

        public ISmartpart DebugControl
        {
            get
            {
                return pDebugControl;
            }
        }

        public IModule Module
        {
            get 
            {
                return this;
            }
        }

		#endregion

		#region Initialization

        public override void Initialize()
        {
            if (HostType != ModuleHostType.WindowsForms)
            {
                return;
            }

			pStateManagementService = Services.Get<IStateManagementService>();

            pDebugControl = new DebugControl(this);
            (pDebugControl as DebugControl).DirectoryService = pDirectoryService;
#if !DEBUG
			pDebugControl.IsVisible = false;
#endif
            Host.Smartparts.Add(pDebugControl);

			pStateManagementService.StateChanged -= new EventHandler<SBPweb.Contracts.StateManagement.StateEventArgs>(StateManagement_StateChanged);
			pStateManagementService.StateChanged += new EventHandler<SBPweb.Contracts.StateManagement.StateEventArgs>(StateManagement_StateChanged);

            pMessage = pStateManagementService.Message;
            pStateManagementService.MessageChanged += new EventHandler<SBPweb.Contracts.StateManagement.MessageEventArgs>(pStateManagementService_MessageChanged);

			if (EasterEggHandler != null)
			{
				debugEasterEgg = EasterEggHandler.RegisterEasterEgg("dbg", new EggCallBackDelegate(DebugWindow_EasterEggRaised));
			}

            pJID = new LabelItemControl();

            Host.StatusBar.Add(pJID);

			if (loginControl == null && Host != null)
			{
				loginControl = new LoginDockingControl(this);
				Host.Smartparts.Add(loginControl);
			}

        }

		#endregion

		#region Public methods

		public void Connect(string user, string password)
        {
			pDirectoryService.Connection.SetConnectionDefaults();
            pDirectoryService.Connection.Connect(user, password);
        }

		internal void Connect(string user, string password, string server, string conference, string pubsub, int port)
		{
			(pDirectoryService.Configuration as Verety.DirectoryService.Configuration.DirectoryConfigurationManager).Server = server;
			(pDirectoryService.Configuration as Verety.DirectoryService.Configuration.DirectoryConfigurationManager).ConferenceServer = conference;
			(pDirectoryService.Configuration as Verety.DirectoryService.Configuration.DirectoryConfigurationManager).PubSubServer = pubsub;
			(pDirectoryService.Configuration as Verety.DirectoryService.Configuration.DirectoryConfigurationManager).Port = port;
			Connect(user, password);
		}

		public void Disconnect()
		{
			pDirectoryService.Connection.Disconnect();
		}

        public void SetDirectoryState(string state)
        {
            pLastState = state;
            DirectoryStatePresence presence = pDirectoryService.Connection.DefaultPresence.Clone() as DirectoryStatePresence;
            if (string.IsNullOrEmpty(pMessage))
            {
                presence["status"].InnerText = state;
            }
            else
            {
                string message = pMessage;

                message = message.Replace("<", "&lt;");
                message = message.Replace(">", "&gt;");

                presence["status"].InnerText = state + "::" + message;
            }
            pDirectoryService.Connection.SetCurrentUserState(presence);
        }

        #endregion

		#region Public overridden methods

		public override void LoadPersistency()
		{
			IStatePersistenceService persistenceService = Services.Get<IStatePersistenceService>();
			if (persistenceService != null)
			{
				PersistencyDictionary state = persistenceService.Load(this);
				loginControl.LoadPersistence(state);
			}
		}

		public override void SavePersistency()
		{
			IStatePersistenceService persistenceService = Services.Get<IStatePersistenceService>();
			if (persistenceService != null)
			{
				PersistencyDictionary state = persistenceService.Load(this);
				loginControl.SavePersistence(state);
				persistenceService.Save(this, state);
			}
		}

        public override void Terminate()
        {
			pDirectoryService.Connection.Disconnect();

            pDirectoryService.Connection.OnDisconnect -= new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnDisconnect);
            pDirectoryService.Connection.OnAuthenticate -= new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnAuthenticate);
            pDirectoryService.Connection.OnAuthenticationError -= new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionHandler(Connection_OnAuthenticationError);
            pDirectoryService.Connection.OnWarning -= new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionErrorHandler(Connection_OnWarning);
            pDirectoryService.Connection.OnError -= new Verety.SharedMeta.MessagingServiceMeta.Connection.ConnectionErrorHandler(Connection_OnError);

			pStateManagementService.StateChanged -= new EventHandler<SBPweb.Contracts.StateManagement.StateEventArgs>(StateManagement_StateChanged);

            if (pDebugControl != null)
            {
                try
                {
                    pDebugControl.HideOnlyWhenClose = false;
                    Host.Smartparts.Remove(pDebugControl);
                }
                catch
                {
                    //do nothing
                }
                //pDebugControl.Dispose();
                pDebugControl = null;
            } 
            
            if (loginControl != null)
			{
                try
                {
                    loginControl.HideOnlyWhenClose = false;
                    Host.Smartparts.Remove(loginControl);
                }
                catch
                {
                    //do nothing
                }
				//loginControl.Dispose();
				loginControl = null;
			}

            Host.StatusBar.Remove(pJID);
            pJID = null;

			if (debugEasterEgg != null && EasterEggHandler != null)
			{
				EasterEggHandler.UnRegisterEasterEgg(debugEasterEgg);
				debugEasterEgg = null;
			}

        }

        public override void Dispose()
        {
			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWrite -= new Verety.SharedMeta.MessagingServiceMeta.Log.LogWrite(LogManager_OnLogWrite);
			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWriteMultiCategory -= new Verety.SharedMeta.MessagingServiceMeta.Log.LogWriteMultiCategory(LogManager_OnLogWriteMultiCategory);
			Verety.SharedMeta.MessagingServiceMeta.Log.LogManager.OnLogWriteUniCategory -= new Verety.SharedMeta.MessagingServiceMeta.Log.LogWriteUniCategory(LogManager_OnLogWriteUniCategory);

            pDirectoryService.Finalize();
            pDirectoryService.Dispose();
            pDirectoryService = null;
            Services.Remove(typeof(IMessagingService));
            base.Dispose();
        }

		#endregion

		#region Easter egg handler

		private void DebugWindow_EasterEggRaised()
		{
			pDebugControl.IsVisible = true;
		}

		#endregion

		#region DirectoryService log handling

		private void LogManager_OnLogWriteUniCategory(Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity severity, string message, string category)
		{
			LogDirectoryLog(severity, category, message);
		}

		private void LogManager_OnLogWriteMultiCategory(Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity severity, string message, List<string> categories)
		{
			string category = string.Empty;
			foreach (string cat in categories)
			{
				if (category != string.Empty)
				{
					category += ", ";
				}
				category += cat;
			}
			if (category != string.Empty)
			{
				category += ": ";
			}
			LogDirectoryLog(severity, category, message);
		}

		private void LogManager_OnLogWrite(Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity severity, string message)
		{
			LogDirectoryLog(severity, string.Empty, message);
		}

		private void LogDirectoryLog(Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity severity, string category, string message)
		{
			switch (severity)
			{
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Critical:
					Host.LogManager.WriteCritical(pDirectoryService, message, "DirectoryService");
					break;
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Error:
					Host.LogManager.WriteError(pDirectoryService, message, "DirectoryService");
					break;
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Info:
					Host.LogManager.WriteInfo(pDirectoryService, message, "DirectoryService");
					break;
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Undefinied:
					Host.LogManager.WriteInfo(pDirectoryService, message, "DirectoryService");
					break;
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Verbose:
					Host.LogManager.WriteVerbose(pDirectoryService, message, "DirectoryService");
					break;
				case Verety.SharedMeta.MessagingServiceMeta.Log.LogSeverity.Warning:
					Host.LogManager.WriteWarning(pDirectoryService, message, "DirectoryService");
					break;
				default:
					break;
			}
		}

		#endregion

		#region Service Event handlers

        protected void ModuleLoader_ModuleGroupLoaded(object sender, EventArgs e)
        {
            if (!IsConnected && mFirstTry)
            {
                new TryToLogInWithADUserHandler(TryToLogInWithADUser).BeginInvoke(null, null);
            }

            mFirstTry = false;
        }

        protected void pStateManagementService_MessageChanged(object sender, SBPweb.Contracts.StateManagement.MessageEventArgs e)
        {
            pMessage = e.Message;
            if (this.IsConnected)
            {
                SetDirectoryState(pLastState);
            }
        }

		protected void StateManagement_StateChanged(object sender, SBPweb.Contracts.StateManagement.StateEventArgs e)
		{
			if (this.IsConnected)
			{
				SetDirectoryState(e.NewState.DirectoryStateName);
			}
		}

		protected void Connection_OnDisconnect(object sender)
		{
			if ((Host as Control).InvokeRequired)
			{
				(Host as Control).Invoke(new ConnectionHandler(Connection_OnDisconnect), sender);
				return;
			}

            loginControl.IsVisible = true;

            pJID.Text = "";

//#if DEBUG
//            pDebugControl.IsVisible = false;
//#endif

			if (OnDisconnect != null)
				OnDisconnect(this);
		}

		protected void Connection_OnAuthenticate(object sender)
		{
			if ((Host as Control).InvokeRequired)
			{
				(Host as Control).Invoke(new ConnectionHandler(Connection_OnAuthenticate), sender);
				return;
			}

			if (pStateManagementService != null)
			{
				SetDirectoryState(pStateManagementService.CurrentState.DirectoryStateName);
			}
            pDirectoryService.Contacts.GetContactList();
            pJID.Text = pDirectoryService.Connection.UserContactId.ToString();

            loginControl.IsVisible = false;

//#if DEBUG
//            pDebugControl.IsVisible = true;
//            (Host as Control).BringToFront();
//#endif
            
            if (OnAutenticate != null)
				OnAutenticate(this);
		}

		protected void Connection_OnAuthenticationError(object sender)
		{
			if ((Host as Control).InvokeRequired)
			{
				(Host as Control).Invoke(new ConnectionHandler(Connection_OnAuthenticationError), sender);
				return;
			}
			if (OnAuthenticationError != null)
				OnAuthenticationError(this);
		}

		protected void Connection_OnWarning(object sender, string message)
		{
			if ((Host as Control).InvokeRequired)
			{
				(Host as Control).Invoke(new ConnectionErrorHandler(Connection_OnWarning), sender, message);
				return;
			}
			if (OnWarning != null)
				OnWarning(this, message);
		}

		protected void Connection_OnError(object sender, string message)
		{
			if ((Host as Control).InvokeRequired)
			{
				(Host as Control).Invoke(new ConnectionErrorHandler(Connection_OnError), sender, message);
				return;
			}

            if (!loginControl.IsVisible)
                loginControl.IsVisible = true;

            pJID.Text = "";
			if (OnError != null)
				OnError(this, message);
		}

		#endregion

		#region Events

		public event ConnectionHandler OnDisconnect;
		public event ConnectionHandler OnAutenticate;
		public event ConnectionHandler OnAuthenticationError;
		public event ConnectionErrorHandler OnWarning;
		public event ConnectionErrorHandler OnError;

		#endregion
	}
}
